package org.kwin.ibatis.config;

import org.kwin.ibatis.proxy.MapperProxyFactory;
import org.kwin.ibatis.session.DefaultSqlSessionFactory;
import org.kwin.ibatis.session.SqlConfig;
import org.kwin.spring.beans.annotation.*;
import org.kwin.spring.beans.factory.AbstractActionFactory;
import org.kwin.spring.beans.factory.BeanFactory;
import org.kwin.spring.context.ActionApplicationContext;
import org.kwin.spring.context.ApplicationContext;
import org.kwin.spring.jdbc.config.OrmAutoConfigure;
import org.kwin.spring.jdbc.session.SqlSessionFactory;
import org.kwin.spring.web.annotation.Controller;
import org.kwin.spring.web.handler.HandlerMapping;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author : wangkai
 * @version V1.0
 * @Package org.kwin.ibatis.config
 * @Description:
 * @date 2021年09月28日 10:23
 **/
public class MybatisConfig implements OrmAutoConfigure {
    
    private ApplicationContext applicationContext;
    
    @Override
    public void auto(ApplicationContext applicationContext, Annotation annotation, Properties properties) {
        this.applicationContext = applicationContext;
        MapperScan mapperScan =  (MapperScan)annotation;
//        ApplicationContext.getIoc();
        applicationContext.getIoc().put(SqlSessionFactory.class.getName(),
            new DefaultSqlSessionFactory(new SqlConfig(properties)));
    
        String[] paths = mapperScan.basePackages();
//        MapperProxyFactory.getProxy();
        for(String path : paths) {
            loadComponent(path);
        }
    }
    
    private void loadComponent(String basePkg) {
        try {
            String path = basePkg.replaceAll("\\.", "/");
            
            Enumeration<URL> urls = this.getClass().getClassLoader().getResources(basePkg.replaceAll("\\.", "/"));
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if(url == null) {
                    continue;
                }
                loadComponent(basePkg, url);
                
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
    
    private void loadComponent(String basePkg, URL url) {
        if(url == null) {
            return;
        }
        String protocol = url.getProtocol();
        if("file".equalsIgnoreCase(protocol)) {
            String packagePath = url.getPath().replaceAll("%20", " ");
            loadComponentByPath(basePkg, packagePath);
        }
        if("jar".equalsIgnoreCase(protocol)) {
            loadComponentByJar(url);
        }
    }
    
    private void loadComponentByJar(URL url) {
        try {
            JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
            if (jarURLConnection == null) {
                return;
            }
            JarFile jarFile = jarURLConnection.getJarFile();
            if (jarFile == null) {
                return;
            }
            Enumeration<JarEntry> jarEntries = jarFile.entries();
            while (jarEntries.hasMoreElements()) {
                JarEntry jarEntry = jarEntries.nextElement();
                String jarEntryName = jarEntry.getName();
                if (jarEntryName.endsWith(".class")) {
                    String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("\\\\", ".").replaceAll("/", ".");
                    loadComponentByClzz(className);
                }
            }
            
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private void loadComponentByPath(String basePkg, String pkgPath) {
        File[] files = new File(pkgPath).listFiles(file -> (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory());
        for(File file : files) {
            String fileName = file.getName();
            if(file.isDirectory()) {
                String childPkgPath = pkgPath + ActionApplicationContext.SEPARATE + fileName;
                loadComponentByPath(basePkg + "." + fileName, childPkgPath);
            } else {
                String className = fileName.substring(0, fileName.lastIndexOf("."));
                
               className = basePkg + "." + className;
                loadComponentByClzz(className);
            }
        }
        
    }
    
    private void loadComponentByClzz(String clzzName) {
        try {
            Class clzz = Class.forName(clzzName);
            if (clzz.isAnnotationPresent(Mapper.class))
            {
                loadComponent(clzz);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    private void loadComponent(Class clzz) {
        Object obj = MapperProxyFactory.getProxy(clzz);
        applicationContext.getIoc().put(clzz.getName(), obj);
//        Map<String,Object> map = applicationContext.getIoc();
//        System.out.println("IOC : " + applicationContext.getIoc());
    }
}
